#include <stdbool.h>
#include <caml/mlvalues.h>
#include <caml/fail.h>
#include <caml/memory.h>
#include <caml/signals.h>
#include <porttime.h>

static PtError pt_try(PtError e) {
	switch (e) {
		case ptHostError:
			caml_failwith("PortTime: Host Error");
		case ptAlreadyStarted:
			caml_failwith("PortTime: Already Started");
		case ptAlreadyStopped:
			caml_failwith("PortTime: Already Stopped");
		case ptInsufficientMemory:
			caml_failwith("PortTime: Insufficient Memory");
		default:
			return e;
	}
}

CAMLprim value caml_Pt_Start_Simple(value resolution) {
	CAMLparam1(resolution);
	pt_try(Pt_Start(Int_val(resolution), NULL, NULL));
	CAMLreturn(Val_unit);
}

static value pt_fun = Val_unit;

static void pt_callback(PtTimestamp timestamp, value *fun) {
	caml_leave_blocking_section();
	/* TODO: should we check for pt_fun block-ness here? */
	caml_callback(*fun, Val_int(timestamp));
	caml_enter_blocking_section();
}

CAMLprim value caml_Pt_Start(value resolution, value fun) {
	CAMLparam2(resolution, fun);
	
	if (Is_block(fun)) caml_register_global_root(&pt_fun);
	pt_fun = fun;
	pt_try(Pt_Start(Int_val(resolution), (PtCallback *) pt_callback, &pt_fun));
		
	CAMLreturn(Val_unit);
}

CAMLprim value caml_Pt_Stop(value unit) {
	CAMLparam1(unit);
	
	pt_try(Pt_Stop());
	if (Is_block(pt_fun)) caml_remove_global_root(&pt_fun);
	
	CAMLreturn(Val_unit);
}

CAMLprim value caml_Pt_Started(value unit) {
	CAMLparam1(unit);
	CAMLreturn(Val_bool(Pt_Started()));
}

CAMLprim value caml_Pt_Time(value unit) {
	CAMLparam1(unit);
	CAMLreturn(Val_int(Pt_Time()));
}
